﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using COFE.Core;
using COFE.Core.Profile;
using COFE.Core.Utils;

namespace COFE.Core.Lister
{
    public class DirectoryControllerBase<T> : IDirectoryController
    {
        #region Constructor

        public DirectoryControllerBase(DirectoryListerBase<T> directoryLister)
        {
            _directoryLister = directoryLister;            
        }

        #endregion

        #region Methods        

        protected T getEmbeddedItem(IInternalFileSystemInfoExA entry)
        {
            return EntryUtils.GetEmbeddedEntry<T>(entry);
        }



        public virtual IInternalFileInfoExA CreateFile(IInternalDirectoryInfoExA parent, string fileName)
        {
            string parentParseName = parent.GetParseNameOrDefault();
            T embeddedEntry = getEmbeddedItem(parent);
            T createdFile = _createFile(embeddedEntry, fileName);            
            IInternalFileInfoExA createdEntry =
                COFEShared.EntryConstructor.CreateFile(ListerKey,  
                _directoryLister.CreatePropertyProvider(createdFile));
            NotifyModified(createdEntry, ModifiedAction.Created);
            return createdEntry;
        }

        public virtual IInternalDirectoryInfoExA CreateDirectory(IInternalDirectoryInfoExA parent, string dirName)
        {
            string parentParseName = parent.GetParseNameOrDefault();
            T embeddedEntry = getEmbeddedItem(parent);
            T createdDirectory = _createDirectory(embeddedEntry, dirName);
            IInternalDirectoryInfoExA createdEntry =
                COFEShared.EntryConstructor.CreateDirectory(ListerKey, _directoryLister.CreatePropertyProvider(createdDirectory));
            NotifyModified(createdEntry, ModifiedAction.Created);
            return createdEntry;
        }

        public virtual void Rename(IInternalFileSystemInfoExA entry, string newName)
        {
            string oldParsePath = entry.ParseName;
            string newParsePath = COFEPath.Combine(COFEPath.GetDirectoryName(oldParsePath), newName);

            T embeddedEntry = getEmbeddedItem(entry);
            _rename(embeddedEntry, newName);

            NotifyModified(newParsePath, entry.IsFolder, ModifiedAction.Renamed, oldParsePath);
        }

        public virtual void Copy(IInternalFileSystemInfoExA entry, IInternalDirectoryInfoExA targetDir, bool raiseAlert)
        {
            //Both source and target are within same directory volume.
            if (_copy != null && entry.ListerKey == ListerKey)
            {
                T embeddedEntry = getEmbeddedItem(entry);
                T embeddedTargetEntry = getEmbeddedItem(targetDir);
                _copy(embeddedEntry, embeddedTargetEntry);                
            }
            else
                if (entry is IFileInfo)
                    using (var inputStream = (entry as IFileInfo).OpenRead())
                    using (var outputStream = COFEShared.CurrentUserProfile.File.OpenWrite(COFEPath.Combine(targetDir.ParseName, entry.Name)))
                        StreamUtils.CopyStream(inputStream, outputStream, false, false, false);
                else
                {
                    var targetCreatedDir = CreateDirectory(targetDir, entry.Name);
                    foreach (var fsi in (entry as IDirectoryInfo).GetFileSystemInfos())
                        Copy(fsi as IInternalFileSystemInfoExA, targetCreatedDir, false);
                }

            NotifyModified(COFEPath.Combine(targetDir.ParseName, entry.Name), entry.IsFolder, ModifiedAction.Created);
            
        }

        public virtual void Move(IInternalFileSystemInfoExA entry, IInternalDirectoryInfoExA targetDir, bool raiseAlert)
        {
            //Both source and target are within same directory volume.
            if (_move != null && entry.ListerKey == ListerKey)
            {
                T embeddedEntry = getEmbeddedItem(entry);
                T embeddedTargetEntry = getEmbeddedItem(targetDir);
                _move(embeddedEntry, embeddedTargetEntry);
            }
            else
            {
                Copy(entry, targetDir, raiseAlert);
                entry.Delete();
                NotifyModified(entry.ParseName, entry.IsFolder, ModifiedAction.Deleted);
            }            
        }

        public virtual void Delete(IInternalFileSystemInfoExA entry, bool raiseAlert)
        {
            try
            {
                T embeddedEntry = getEmbeddedItem(entry);
                _delete(embeddedEntry);
            }
            catch (System.IO.FileNotFoundException) { /* Already somehow deleted. */ }

            if (raiseAlert)
            {
                INotifyFileSystemChanged delItem = (INotifyFileSystemChanged)entry;
                delItem.AlertModified(ModifiedAction.Deleted);
            }
            NotifyModified(entry.ParseName, entry.IsFolder, ModifiedAction.Deleted);
        }

        public virtual void NotifyModified(string parsePath, bool isFolder, ModifiedAction action, string oldParsePath = null)
        {
            if (_notifyModified != null)
            {
                IInternalFileSystemInfoExA entry = COFEShared.PathParser.TryParsePath(parsePath, ParsableType.ParsePath) as IInternalFileSystemInfoExA;
                if (entry != null)
                    NotifyModified(entry, action, oldParsePath);
            }
            else
            {
                if (action == ModifiedAction.Renamed)
                    COFEShared.FileSystemEventDispatcher.DispatchEvent(this , new Logger.RenameEventArgsExA(ListerKey, action, isFolder, parsePath, oldParsePath));
                else
                    COFEShared.FileSystemEventDispatcher.DispatchEvent(this, new Logger.FileSystemEventArgsExA(ListerKey, action, isFolder, parsePath));
            }
                
        }

        public virtual void NotifyModified(IInternalFileSystemInfoExA entry, ModifiedAction action, string oldParsePath = null)
        {
            //Console.WriteLine(String.Format("[{0}] - {1}", action, entries.ToString()));
            if (_notifyModified != null)
            {
                try
                {
                    T embeddedEntry = getEmbeddedItem(entry);
                    _notifyModified(embeddedEntry, action);
                }
                catch (System.IO.FileNotFoundException)
                {

                }
            }

            if (action == ModifiedAction.Renamed)
                COFEShared.FileSystemEventDispatcher.DispatchEvent(this, new Logger.RenameEventArgsExA(ListerKey, action, entry.IsFolder, entry.ParseName, oldParsePath));
            else
                COFEShared.FileSystemEventDispatcher.DispatchEvent(this, new Logger.FileSystemEventArgsExA(ListerKey, action, entry.IsFolder, entry.ParseName));
        }

        #endregion

        #region Data

        protected Func<T, string, T> _createFile = (item, name) => { throw new NotImplementedException(); };
        protected Func<T, string, T> _createDirectory = (item, name) => { throw new NotImplementedException(); };
        protected Action<T, string> _rename = (item, name) => { throw new NotImplementedException(); };
        protected Action<T, ModifiedAction> _notifyModified = null;
        protected Action<T, T> _copy = null;
        protected Action<T, T> _move = null;
        protected Action<T> _delete = (item) => { throw new NotImplementedException(); };
        
        protected DirectoryListerBase<T> _directoryLister;
       

        #endregion

        #region Public Properties

        public string ListerKey { get { return _directoryLister.ListerKey; } }


        #endregion


    }
}
